﻿using AdminModels;
using Entities;
using Entities.CoinEntities.Cryptsy;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoinModels.Cryptsy
{
    [JsonObject]
    public class CryptsyCoin : iBasicCoinValues
    {
        [JsonIgnore]
        public int id { get; set; }

        [JsonProperty(Required = Required.Always)]
        public string label { get; set; }

        [JsonProperty(Required = Required.AllowNull)]
        public double? lasttradeprice { get; set; }

        [JsonIgnore]
        public DateTime? lastTradeTime { get; set; }

        [JsonProperty(Required = Required.AllowNull)]
        public string lasttradetime
        {
            set
            {
                DateTime temp;
                bool worked = DateTime.TryParse(value, out temp);
                if (worked)
                {
                    lastTradeTime = temp;
                }
                else
                {
                    lastTradeTime = null;
                }
            }
        }

        [JsonProperty(Required = Required.Always)]
        public int marketid { get; set; }

        [JsonProperty(Required = Required.Always)]
        public string primarycode { get; set; }

        [JsonProperty(Required = Required.Always)]
        public string primaryname { get; set; }

        [JsonProperty(Required = Required.Always)]
        public string secondarycode { get; set; }

        [JsonProperty(Required = Required.Always)]
        public string secondaryname { get; set; }

        [JsonProperty("volume", Required = Required.Always)]
        public double vol { get; set; }

        [JsonProperty]
        public List<RecentTrade> recenttrades { get; set; }

        [JsonIgnore]
        private List<SellOrder> sellOrders { get; set; }

        [JsonProperty]
        public List<SellOrder> sellorders
        {
            get
            {
                if (sellOrders == null)
                {
                    sellOrders = new List<SellOrder>();
                }

                return sellOrders;
            }

            set
            {
                sellOrders = value;
            }
        }

        [JsonIgnore]
        private List<BuyOrder> buyOrders { get; set; }

        [JsonProperty]
        public List<BuyOrder> buyorders
        {
            get
            {
                if (buyOrders == null)
                {
                    buyOrders = new List<BuyOrder>();
                }

                return buyOrders;
            }

            set
            {
                buyOrders = value;
            }
        }

        [JsonIgnore]
        public double low { get; set; }

        [JsonIgnore]
        public double high { get; set; }

        [JsonIgnore]
        public double avg { get; set; }

        [JsonIgnore]
        public DateTime updatedat { get; set; }

        /// <summary>
        /// Persists coins to the DB. Throws an error if it fails.
        /// Will return false and log an error later
        /// </summary>
        /// <param name="coinsToPersist">List of Coins to Persist</param>
        /// <returns>If it succeeded</returns>
        public static bool PersistCoinCollection(List<CryptsyCoin> coinsToPersist)
        {
            bool success = false;
            Exception exc = null;

            using (AegirContext db = new AegirContext())
            {
                foreach (CryptsyCoin item in coinsToPersist)
                {
                    item.updatedat = DateTime.Now;
                    item.avg = item.GetAverage();
                    item.high = item.GetHigh();
                    item.low = item.GetLow();
                    db.Cryptsy_Coin.Add(ConvertToEntity(item));
                }
                try
                {
                    db.SaveChanges();
                    success = true;
                }
                catch (Exception ex)
                {
                    exc = ex;
                    success = false;
                    throw;
                }
            }

            if (exc != null)
                    ErrorLog.SaveLog(exc, "Cryptsy.Coin", ErrorType.Persistance);

            return success;
        }

        /// <summary>
        /// Calculates the average coin value
        /// </summary>
        /// <returns>The average coin value</returns>
        private double GetAverage()
        {
            double retVal = 0, total = 0, count = 0;

            if (buyorders != null)
            {
                foreach (BuyOrder order in buyorders)
                {
                    total += order.total;
                    count += order.quantity;
                }
            }

            if (sellorders != null)
            {
                foreach (SellOrder order in sellorders)
                {
                    total += order.total;
                    count += order.quantity;
                }
            }

            if (count > 0)
            {
                retVal = total / count;
            }

            return retVal;
        }

        /// <summary>
        /// Calculates the low coin value
        /// </summary>
        /// <returns>The low coin value</returns>
        private double GetLow()
        {
            double retVal = 0;
            var temp = buyorders.FirstOrDefault();
            SellOrder temp1 = null;
            if (temp == null)
            {
                temp1 = sellorders.FirstOrDefault();
                if (temp1 != null)
                {
                    retVal = temp1.price;
                }
            }
            else
            {
                retVal = temp.price;
            }

            if (!(retVal == 0))
            {
                foreach (BuyOrder order in buyorders)
                {
                    if (order.price < retVal)
                    {
                        retVal = order.price;
                    }
                }

                foreach (SellOrder order in sellorders)
                {
                    if (order.price < retVal)
                    {
                        retVal = order.price;
                    }
                }
            }

            return retVal;
        }


        /// <summary>
        /// Calculates the high coin value
        /// </summary>
        /// <returns>The high coin value</returns>
        private double GetHigh()
        {
            double retVal = 0;
            var temp = buyorders.FirstOrDefault();
            SellOrder temp1 = null;
            if (temp == null)
            {
                temp1 = sellorders.FirstOrDefault();

                if (temp1 != null)
                {
                    retVal = temp1.price;
                }
            }
            else
            {
                retVal = temp.price;
            }

            if (!(retVal == 0))
            {
                foreach (BuyOrder order in buyorders)
                {
                    if (order.price > retVal)
                    {
                        retVal = order.price;
                    }
                }

                foreach (SellOrder order in sellorders)
                {
                    if (order.price > retVal)
                    {
                        retVal = order.price;
                    }
                }
            }

            return retVal;
        }

        /// <summary>
        /// Converts a coin from a model to an entity,
        /// Throws error on validation fail
        /// </summary>
        /// <param name="item">CryptsyCoin to Convert</param>
        /// <returns>Converted Entity</returns>
        private static CryptsyCoinEntity ConvertToEntity(CryptsyCoin item)
        {
            CryptsyCoinEntity retVal = new CryptsyCoinEntity();

            // TODO: Validation
            retVal.avg = item.avg;
            retVal.high = item.high;
            retVal.label = item.label;
            retVal.lasttradeprice = item.lasttradeprice;
            retVal.lasttradetime = item.lastTradeTime;
            retVal.low = item.low;
            retVal.marketid = item.marketid;
            retVal.primarycode = item.primarycode;
            retVal.primaryname = item.primaryname;
            retVal.secondarycode = item.secondarycode;
            retVal.secondaryname = item.secondaryname;
            retVal.updatedat = item.updatedat;
            retVal.volume = item.vol;

            return retVal;
        }

        /// <summary>
        /// Gets a Model for a single coin
        /// </summary>
        /// <param name="primaryCode">Primary Code of Coin</param>
        /// <param name="secondaryCode">Secondary Code of Coin</param>
        /// <returns>Model of coin</returns>
        public static CryptsyCoin GrabCoinModel(string primaryCode, string secondaryCode)
        {
            CryptsyCoinEntity entity;
            CryptsyCoin model = null;

            using (AegirContext db = new AegirContext())
            {
                entity = db.Cryptsy_Coin.Where(btce => btce.primarycode == primaryCode
                    && btce.secondarycode == secondaryCode).SingleOrDefault();
            }

            if (entity != null)
            {
                model = ConvertToModel(entity);
            }

            return model;
        }

        public static CryptsyCoin ConvertToModel(CryptsyCoinEntity entity)
        {
            CryptsyCoin model = new CryptsyCoin();

            model.avg = entity.avg;
            model.high = entity.high;
            model.id = entity.id;
            model.label = entity.label;
            model.lasttradeprice = entity.lasttradeprice;
            model.lastTradeTime = entity.lasttradetime;
            model.low = entity.low;
            model.marketid = entity.marketid;
            model.primarycode = entity.primarycode;
            model.primaryname = entity.primaryname;
            model.secondarycode = entity.secondarycode;
            model.secondaryname = entity.secondaryname;
            model.updatedat = entity.updatedat;
            model.vol = entity.volume;

            return model;
        }
    }
}
